namespace
{
    class xistream_constraint
    {
    public:
        virtual void check( xml::xistream& xis ) = 0;
    };

    typedef MOCKPP_NS::Invocation1< xml::xistream > Invocation;

    class xistream_matcher : public MOCKPP_NS::InvocationMatcher< Invocation >
    {
    public:
        xistream_matcher( xistream_constraint& constraint )
            : constraint_( constraint )
        {}

        virtual bool matches( const Invocation & invocation )
        {
            xml::xistream& xis = const_cast< xml::xistream& >( invocation.getParameter1() );
            constraint_.check( xis );
            return true;
        }

        virtual void verify()
        {
        }

        virtual void incInvoked( const Invocation &/*invocation*/ )
        {
        }

        virtual bool hasDescription()
        {
            return true;
        }

        virtual String describeTo( String &buffer ) const
        {
            buffer += MOCKPP_PCHAR("(xistream_matcher)");
            return buffer;
        }
    private :
        xistream_constraint& constraint_;
    };

    class constraint : public xistream_constraint
    {
    public:
        constraint( const std::string& content )
            : content_( content )
        {}
        virtual ~constraint()
        {}
        virtual void check( xml::xistream& xis )
        {
            std::string content;
            xis >> content;
            BOOST_CHECK_EQUAL( content_, content );
        }
        const std::string content_;
    };

    class mock_custom_class : public MOCKPP_NS::ChainableMockObject
    {
    public:
        mock_custom_class()
            : MOCKPP_NS::ChainableMockObject( "mock_custom_class", 0 )
            , process_mocker             ( "process", this )
        {}
        void process( xml::xistream& xis )
        {
            process_mocker.forward( xis );
        }
        MOCKPP_NS::ChainableMockMethod< void, xml::xistream > process_mocker;
    };
}

// -----------------------------------------------------------------------------
// Name: read_list_from_element_calls_a_custom_method
// Created: MCO 2006-01-03
// -----------------------------------------------------------------------------
BOOST_AUTO_UNIT_TEST( read_list_from_element_calls_a_custom_method )
{
    mock_custom_class mock_custom;
    ChainableMockMethod< void, xml::xistream > &process_chainer( mock_custom.process_mocker );
    constraint c1( "content number one" );
    constraint c2( "content number two" );
    process_chainer.expects( once() ).match( new xistream_matcher( c1 ) );
    process_chainer.expects( once() ).match( new xistream_matcher( c2 ) );
    xml::xistringstream xis( "<element>"
                               "<sub-node>content number one</sub-node>"
                               "<sub-node>content number two</sub-node>"
                             "</element>" );
    xis >> xml::start( "element" )
            >> xml::list( "sub-node", mock_custom, &mock_custom_class::process )
        >> xml::end();
    mock_custom.verify();
}

namespace
{
    class mock_custom_class_with_parameters : public MOCKPP_NS::ChainableMockObject
    {
    public:
        mock_custom_class_with_parameters()
            : MOCKPP_NS::ChainableMockObject( "mock_custom_class", 0 )
            , process_mocker             ( "process", this )
        {}
        void process( xml::xistream& xis, int& p1, const float& p2 )
        {
            BOOST_CHECK_EQUAL( 12, p1 );
            BOOST_CHECK_EQUAL( 12.42f, p2 ); // $$$$ MAT 2006-02-04: forward to the mock instead
            process_mocker.forward( xis );
        }
        MOCKPP_NS::ChainableMockMethod< void, xml::xistream > process_mocker;
    };
}

// -----------------------------------------------------------------------------
// Name: read_list_from_element_calls_a_custom_method_with_parameters
// Created: MCO 2006-01-03
// -----------------------------------------------------------------------------
BOOST_AUTO_UNIT_TEST( read_list_from_element_calls_a_custom_method_with_parameters )
{
    mock_custom_class_with_parameters mock_custom;
    ChainableMockMethod< void, xml::xistream > &process_chainer( mock_custom.process_mocker );
    constraint c1( "content number one" );
    constraint c2( "content number two" );
    process_chainer.expects( once() ).match( new xistream_matcher( c1 ) );
    process_chainer.expects( once() ).match( new xistream_matcher( c2 ) );
    xml::xistringstream xis( "<element>"
                               "<sub-node>content number one</sub-node>"
                               "<sub-node>content number two</sub-node>"
                             "</element>" );
    int p1 = 12;
    const float p2 = 12.42f;
    xis >> xml::start( "element" )
            >> xml::list( "sub-node", mock_custom, &mock_custom_class_with_parameters::process, p1, p2 )
        >> xml::end();
    mock_custom.verify();
}
